package com.mys.maggie.redis.api.config;

import com.mys.maggie.redis.api.util.JackSonUtil;
import com.mys.maggie.redis.api.util.RedisHelpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.mys.maggie.redis.api.util.RedisHelpUtil.swapIfPresentIsNull;


/**
 * desc: 根据配置文件，使用jedis-cluster加载redis实例
 * date:2020/7/3 13:52
 */
@Slf4j
public class JedisClusterConfigService {
    //连接池中最大空闲的连接数
    private static int DEFAULT_MAX_IDLE = 8 ;
    private static int DEFAULT_MAX_TOTAL =8 ;
    private static int DEFAULT_MIN_IDLE =0 ;
    private static int DEFAULT_MAX_WAIT_MILLIS =-1 ;


    /**
     * 1、解析配置文件
     * 2、初始化 构建 JedisPoolConfig，RedisClusterConfiguration，JedisConnectionFactory，RedisTemplate
     * 3、把结果放在 map里面
     * @param properties 参数
     */
     static Map<String, RedisTemplate<String, Object>> buildJedisTemplateInstance(List<MagRedisProperties.Cluster> clusters,
                                                                                  MagRedisProperties properties) {
        Map<String, RedisTemplate<String, Object>> redisTemplateMap = new HashMap<>();
        // Initialize multi cluster
         clusters.forEach(cluster -> {
            try {
                //1、构建jedis连接池
                JedisPoolConfig poolConfig = buildJedisPoolConfig(cluster,properties);
                //2、构建集群信息
                RedisClusterConfiguration clusterConfiguration = buildRedisClusterConfiguration(cluster);
                //3、根据连接池和集群配置信息构建连接工厂
                RedisConnectionFactory connectionFactory = buildJedisConnectionFactory(poolConfig,clusterConfiguration);
                //4、构造redisTemplate
                RedisTemplate<String, Object> redisTemplate = RedisHelpUtil.buildRedisTemplate(connectionFactory,cluster,properties);
                //5、放置map
                redisTemplateMap.put(cluster.getName(),redisTemplate);

            }catch (Exception e){
                log.error(" redis 初始化失败 :",e);
            }
        });

        return redisTemplateMap;
    }

    /**
     * 通用 根据参数构建jedis连接池
     * @param cluster 配置文件
     * @return JedisPoolConfig
     */
    private static JedisPoolConfig buildJedisPoolConfig(MagRedisProperties.Cluster cluster, MagRedisProperties properties){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        Integer maxTotal = swapIfPresentIsNull(cluster.getMaxActive(),properties.getMaxActive(), DEFAULT_MAX_TOTAL );
        Integer maxIdle = swapIfPresentIsNull(cluster.getMaxIdle(),properties.getMaxIdle() , DEFAULT_MAX_IDLE );
        Integer minIdle = swapIfPresentIsNull(cluster.getMinIdle(),properties.getMinIdle() , DEFAULT_MIN_IDLE);
        Integer maxWaitMillis = swapIfPresentIsNull(cluster.getMaxWait(),properties.getMaxWait() , DEFAULT_MAX_WAIT_MILLIS);

        //接池中最大连接数。高版本：maxTotal，低版本：maxActive
        jedisPoolConfig.setMaxTotal(maxTotal);
        //连接池中最少空闲的连接数
        jedisPoolConfig.setMinIdle( minIdle );
        //连接池中最大空闲的连接数
        jedisPoolConfig.setMaxIdle(maxIdle);
        //当连接池资源耗尽时，调用者最大阻塞的时间，超时将跑出异常。单位，毫秒数;默认为-1.表示永不超时。高版本：maxWaitMillis，低版本：maxWait
        jedisPoolConfig.setMaxWaitMillis( maxWaitMillis );

        return jedisPoolConfig;
    }

    /**
     * 集群处理 配置集群信息
     * @param cluster 节点
     * @return RedisClusterConfiguration
     */
    private static RedisClusterConfiguration buildRedisClusterConfiguration(MagRedisProperties.Cluster cluster){
        RedisClusterConfiguration redisClusterConfiguration =new RedisClusterConfiguration();
        List<RedisNode> nodeList = RedisHelpUtil.convertRedisNodeList( cluster.getNodes() );
        redisClusterConfiguration.setClusterNodes( nodeList );
        if (StringUtils.isNotBlank( cluster.getPassword() )){

            redisClusterConfiguration.setPassword( RedisPassword.of(cluster.getPassword()) );
        }
        return redisClusterConfiguration;
    }

    /**
     * 集群处理 构造 JedisConnectionFactory
     * @param poolConfig 连接池
     * @param clusterConfiguration 集群配置
     * @return JedisConnectionFactory
     */
    private static RedisConnectionFactory buildJedisConnectionFactory(JedisPoolConfig poolConfig, RedisClusterConfiguration clusterConfiguration ){
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder  builder =
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();

        builder.poolConfig( poolConfig );
        //通过构造器来构造jedis客户端配置
        JedisClientConfiguration jedisClientConfiguration = builder.build();
        JedisConnectionFactory factory = new JedisConnectionFactory(clusterConfiguration, jedisClientConfiguration);
        factory.afterPropertiesSet();
        return factory;
    }


    /**
     * 单机处理
     * 1、解析配置文件
     * 2、初始化 构建 JedisPoolConfig，RedisClusterConfiguration，JedisConnectionFactory，RedisTemplate
     * 3、把结果放在 map里面
     * @param properties 参数
     */
    static Map<String, RedisTemplate<String, Object>> buildJedisAloneTemplateInstance(List<MagRedisProperties.Cluster> clusters,
                                                                                      MagRedisProperties properties) {
        Map<String, RedisTemplate<String, Object>> redisTemplateMap = new HashMap<>();
        // Initialize multi cluster
        clusters.forEach(cluster -> {
            try {
                //1、构建jedis连接池
                JedisPoolConfig poolConfig = buildJedisPoolConfig(cluster,properties);
                //2、初始化
                RedisStandaloneConfiguration standaloneConfiguration = buildStandaloneConfig(cluster);
                //3、构建单机信息和链接工厂
                RedisConnectionFactory connectionFactory = buildStandaloneConnFactory(standaloneConfiguration,poolConfig);
                //4、构造redisTemplate
                RedisTemplate<String, Object> redisTemplate = RedisHelpUtil.buildRedisTemplate(connectionFactory,cluster,properties);
                //5、放置map
                redisTemplateMap.put(cluster.getName(),redisTemplate);

            }catch (Exception e){
                log.error(" redis 初始化失败 :",e);
            }
        });

        return redisTemplateMap;
    }

    /**
     * 单机处理, 配置 RedisStandaloneConfiguration
     * @param cluster 集群信息
     * @return RedisConnectionFactory
     * @throws Exception 解析节点时异常
     */
    private static RedisStandaloneConfiguration buildStandaloneConfig(MagRedisProperties.Cluster cluster) throws Exception{
        RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
        String password = cluster.getPassword();
        List<String> nodeList = cluster.getNodes();
        String[] nodeArr = nodeList.get(0).split(":");
        if (nodeArr.length <2 ){
            throw new Exception(" redis单机实例IP配置不正确，格式 IP:PORT ");
        }
        standaloneConfig.setHostName( nodeArr[0] );
        standaloneConfig.setPort( Integer.parseInt( nodeArr[1]  )  );
        if (StringUtils.isNotBlank( password )){
            standaloneConfig.setPassword( RedisPassword.of( password ) );
        }
        log.info("初始化redis单机实例 nodeList:"+ JackSonUtil.toJSONString(nodeList));
        return standaloneConfig;
    }

    /**
     * 单机处理，配置 RedisConnectionFactory
     * @param standaloneConfig 集群信息
     * @param poolConfig 链接池配置
     * @return RedisConnectionFactory
     * @throws Exception 解析节点时异常
     */
    private static RedisConnectionFactory buildStandaloneConnFactory( RedisStandaloneConfiguration standaloneConfig,JedisPoolConfig poolConfig) throws Exception{
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder  builder =
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
        builder.poolConfig( poolConfig );
        //通过构造器来构造jedis客户端配置
        JedisClientConfiguration jedisClientConfiguration = builder.build();
        //通过构造器来构造jedis客户端配置
        JedisConnectionFactory factory = new JedisConnectionFactory(standaloneConfig, jedisClientConfiguration);
        factory.afterPropertiesSet();
        return factory;
    }




}
